package cn.car.Util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLHandshakeException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import cn.car.exception.NetServiceException;


/**
* Apache Httpclient 4.0 工具包装类
*
*/
@SuppressWarnings("all")
public class HttpclientUtil {
	private static final String CHARSET_UTF8 = "UTF-8";
	private static final String CHARSET_GBK = "GBK";
	private static final String SSL_DEFAULT_SCHEME = "https";
	private static final int SSL_DEFAULT_PORT = 443;
	
	// 异常自动恢复处理, 使用HttpRequestRetryHandler接口实现请求的异常恢复
	private static HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
		// 自定义的恢复策略
		public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
			// 设置恢复策略，在发生异常时候将自动重试3次
			if (executionCount >= 3) {
				// Do not retry if over max retry count
				return false;
			}
			if (exception instanceof NoHttpResponseException) {
				// Retry if the server dropped connection on us
				return true;
			}
			if (exception instanceof SSLHandshakeException) {
				// Do not retry on SSL handshake exception
				return false;
			}
			HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
			boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
			if (!idempotent) {
				// Retry if the request is considered idempotent
				return true;
			}
			return false;
		}
	};
	// 使用ResponseHandler接口处理响应，HttpClient使用ResponseHandler会自动管理连接的释放，解决了对连接的释放管理
	private static ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
		// 自定义响应处理
		public String handleResponse(HttpResponse response)	throws ClientProtocolException, IOException {
			HttpEntity entity = response.getEntity();
//			InputStreamReader isr=new InputStreamReader(entity.getContent(),"utf-8");
//			BufferedReader in=new BufferedReader(isr);
//			StringBuffer sb=new StringBuffer();
//			String inputLine;
//			while((inputLine=in.readLine())!=null){
//				sb.append(inputLine);
//			}
//			in.close();
//			return sb.toString();
			if (entity != null) {
				String charset = EntityUtils.getContentCharSet(entity) == null ? CHARSET_GBK : EntityUtils.getContentCharSet(entity);
				return new String(EntityUtils.toByteArray(entity), charset);
			} else {
				return null;
			}
		}
	};
	/**
	 * 处理webservice的返回请求
	 */
	private static ResponseHandler<String> soapResponseHandler = new ResponseHandler<String>() {
		// 自定义响应处理
		public String handleResponse(HttpResponse response)	throws ClientProtocolException, IOException {
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String charset = EntityUtils.getContentCharSet(entity) == null ? CHARSET_GBK : EntityUtils.getContentCharSet(entity);
				String t=new String(EntityUtils.toByteArray(entity), charset);
				t=StringUtil.getWebServiceRsp(t);
				return t;
			} else {
				return null;
			}
		}
	};
	/**
	 * Get方式提交,URL中包含查询参数, 格式：http://www.g.cn?search=p&name=s.....
	 *
	 * @param url
	 * 提交地址
	 * @return 响应消息
	 * @throws NetServiceException 
	 */
	public static String get(String url) throws NetServiceException {
		return get(url, null, null);
	}
	/**
	 * Get方式提交,URL中不包含查询参数, 格式：http://www.g.cn
	 *
	 * @param url
	 * 提交地址
	 * @param params
	 * 查询参数集, 键/值对
	 * @return 响应消息
	 * @throws NetServiceException 
	 */
	public static String get(String url, Map<String, String> params) throws NetServiceException {
		return get(url, params, null);
	}
	/**
	 * Get方式提交,URL中不包含查询参数, 格式：http://www.g.cn
	 *
	 * @param url
	 * 提交地址
	 * @param params
	 * 查询参数集, 键/值对
	 * @param charset
	 * 参数提交编码集
	 * @return 响应消息
	 * @throws NetServiceException 
	 */
	public static String get(String url, Map<String, String> params, String charset) throws NetServiceException {
		if (url == null || StringUtil.isEmpty(url)) {
			return null;
		}
		List<NameValuePair> qparams = getParamsList(params);
		if (qparams != null && qparams.size() > 0) {
			charset = (charset == null ? CHARSET_GBK : charset);
			String formatParams = URLEncodedUtils.format(qparams, charset);
			url = (url.indexOf("?")) < 0 ? (url + "?" + formatParams) : (url
					.substring(0, url.indexOf("?") + 1) + formatParams);
		}
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		HttpGet hg = new HttpGet(url);
		// 发送请求，得到响应
		String responseStr = null;
		try {
			responseStr = httpclient.execute(hg, responseHandler);
		} catch (ClientProtocolException e) {
			throw new NetServiceException("客户端连接协议错误", e);
		} catch (IOException e) {
			throw new NetServiceException("IO操作异常", e);
		} finally {
			abortConnection(hg, httpclient);
		}
		return responseStr;
	}
	/**
	 * Post方式提交,URL中不包含提交参数, 格式：http://www.g.cn
	 *
	 * @param url
	 * 提交地址
	 * @param params
	 * 提交参数集, 键/值对
	 * @return 响应消息
	 * @throws NetServiceException 
	 */
	public static String post(String url, Map<String, String> params) throws NetServiceException {
		return post(url, params, null);
	}
	/**
	 * 提交xml请求
	 * @author hanzhg 2011-11-18 下午02:01:39
	 *
	 * @param url 链接地址
	 * @param params 参数
	 * @param xml xml字符串
	 * @param charset 字符编码
	 * @return
	 * @throws NetServiceException
	 */
	public static String postXML(String url, Map<String, String> params,String xml,String charset) throws NetServiceException {
		if (url == null || StringUtil.isEmpty(url)) {
			return null;
		}
		List<NameValuePair> qparams = getParamsList(params);
		if (qparams != null && qparams.size() > 0) {
			charset = (charset == null ? CHARSET_GBK : charset);
			String formatParams = URLEncodedUtils.format(qparams, charset);
			url = (url.indexOf("?")) < 0 ? (url + "?" + formatParams) : (url
					.substring(0, url.indexOf("?") + 1) + formatParams);
		}
		// 创建HttpClient实例
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		String responseStr = null;
		HttpPost hp = new HttpPost(url);
		StringEntity xmlentity =null;
		try {
			if (charset == null || StringUtil.isEmpty(charset)) {
				xmlentity= new StringEntity(xml); //设置xml 
			} else {
				xmlentity = new StringEntity(xml,charset); //设置xml 
			}
			hp.addHeader("Content-Type", "text/xml; charset="+charset);  
			// 发送请求，得到响应
			hp.setEntity(xmlentity);
			responseStr = httpclient.execute(hp, responseHandler);
		} catch (UnsupportedEncodingException e) {
			throw new NetServiceException("不支持的编码集", e);
		}catch (ClientProtocolException e) {
			throw new NetServiceException("客户端连接协议错误", e);
		} catch (IOException e) {
			throw new NetServiceException("IO操作异常", e);
		} finally {
			abortConnection(hp, httpclient);
		}
		return responseStr;
	}
	/**
	 * 发送webservice请求。
	 * 只适合于一个string参数输入和返回接口，如果是复杂对象的输入和输出，建议还是用工具生成桩代码开发
	 * @author hanzhg 2011-11-23 下午10:51:56
	 *
	 * @param url 接口地址
	 * @param xml 方法的参数字符串
	 * @param charset 编码
	 * @return
	 * @throws NetServiceException
	 */
	public static String postSOAP(String url,String xml,String charset) throws NetServiceException {
		if (url == null || StringUtil.isEmpty(url)) {
			return null;
		}
		// 创建HttpClient实例
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		String responseStr = null;
		StringBuffer envbuf=new StringBuffer();
		envbuf.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><SOAP-ENV:Body>");
		envbuf.append(xml);//加上请求body
		envbuf.append("</SOAP-ENV:Body></SOAP-ENV:Envelope>");//加上结尾
		HttpPost hp = new HttpPost(url);
		try {
			StringEntity xmlentity = new StringEntity(envbuf.toString(),charset); //设置xml 
			hp.addHeader("Content-Type", "application/soap+xml;text/xml; charset="+charset);  
			// 发送请求，得到响应
			hp.addHeader("SOAPAction","");//axis请求必须加上这个
			hp.setEntity(xmlentity);
			responseStr = httpclient.execute(hp, soapResponseHandler);
		} catch (UnsupportedEncodingException e) {
			throw new NetServiceException("不支持的编码集", e);
		}catch (ClientProtocolException e) {
			throw new NetServiceException("客户端连接协议错误", e);
		} catch (IOException e) {
			throw new NetServiceException("IO操作异常", e);
		} finally {
			abortConnection(hp, httpclient);
		}
		return responseStr;
	}
	/**
	 * Post方式提交,URL中不包含提交参数, 格式：http://www.g.cn
	 *
	 * @param url
	 * 提交地址
	 * @param params
	 * 提交参数集, 键/值对
	 * @param charset
	 * 参数提交编码集
	 * @return 响应消息
	 * @throws NetServiceException 
	 */
	public static String post(String url, Map<String, String> params, String charset) throws NetServiceException {
		if (url == null || StringUtil.isEmpty(url)) {
			return null;
		}
		// 创建HttpClient实例
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		UrlEncodedFormEntity formEntity = null;
		try {
			if (charset == null || StringUtil.isEmpty(charset)) {
				formEntity = new UrlEncodedFormEntity(getParamsList(params));
			} else {
				formEntity = new UrlEncodedFormEntity(getParamsList(params), charset);
			}
		} catch (UnsupportedEncodingException e) {
			throw new NetServiceException("不支持的编码集", e);
		}
		HttpPost hp = new HttpPost(url);
		hp.setEntity(formEntity);
		// 发送请求，得到响应
		String responseStr = null;
		try {
			responseStr = httpclient.execute(hp, responseHandler);
		} catch (ClientProtocolException e) {
			throw new NetServiceException("客户端连接协议错误", e);
		} catch (IOException e) {
			throw new NetServiceException("IO操作异常", e);
		} finally {
			abortConnection(hp, httpclient);
		}		
		return responseStr;
	}
	/**
	 * Post方式提交,忽略URL中包含的参数,解决SSL双向数字证书认证
	 *
	 * @param url
	 * 提交地址
	 * @param params
	 * 提交参数集, 键/值对
	 * @param charset
	 * 参数编码集
	 * @param keystoreUrl
	 * 密钥存储库路径
	 * @param keystorePassword
	 * 密钥存储库访问密码
	 * @param truststoreUrl
	 * 信任存储库绝路径
	 * @param truststorePassword
	 * 信任存储库访问密码, 可为null
	 * @return 响应消息
	 * @throws NetServiceException
	 */
	public static String post(String url, Map<String, String> params, String charset, final URL keystoreUrl,
			final String keystorePassword, final URL truststoreUrl,	final String truststorePassword) throws NetServiceException {
		if (url == null || StringUtil.isEmpty(url)) {
			return null;
		}
		DefaultHttpClient httpclient = getDefaultHttpClient(charset);
		UrlEncodedFormEntity formEntity = null;
		try {
			if (charset == null || StringUtil.isEmpty(charset)) {
				formEntity = new UrlEncodedFormEntity(getParamsList(params));
			} else {
				formEntity = new UrlEncodedFormEntity(getParamsList(params), charset);
			}
		} catch (UnsupportedEncodingException e) {
			throw new NetServiceException("不支持的编码集", e);
		}
		HttpPost hp = null;
		String responseStr = null;
		try {
			KeyStore keyStore = createKeyStore(keystoreUrl, keystorePassword);
			KeyStore trustStore = createKeyStore(truststoreUrl, keystorePassword);
			SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore,	keystorePassword, trustStore);
			Scheme scheme = new Scheme(SSL_DEFAULT_SCHEME, socketFactory, SSL_DEFAULT_PORT);
			httpclient.getConnectionManager().getSchemeRegistry().register(scheme);
			hp = new HttpPost(url);
			hp.setEntity(formEntity);
			responseStr = httpclient.execute(hp, responseHandler);			
		} catch (NoSuchAlgorithmException e) {
			throw new NetServiceException("指定的加密算法不可用", e);
		} catch (KeyStoreException e) {
			throw new NetServiceException("keytore解析异常", e);
		} catch (CertificateException e) {
			throw new NetServiceException("信任证书过期或解析异常", e);
		} catch (FileNotFoundException e) {
			throw new NetServiceException("keystore文件不存在", e);
		} catch (IOException e) {
			throw new NetServiceException("I/O操作失败或中断 ", e);
		} catch (UnrecoverableKeyException e) {
			throw new NetServiceException("keystore中的密钥无法恢复异常", e);
		} catch (KeyManagementException e) {
			throw new NetServiceException("处理密钥管理的操作异常", e);
		} finally {
			abortConnection(hp, httpclient);
		}
		return responseStr;
	}
	/**
	 * 获取DefaultHttpClient实例
	 *
	 * @param charset
	 * 参数编码集, 可空
	 * @return DefaultHttpClient 对象
	 */
	public static DefaultHttpClient getDefaultHttpClient(final String charset){
		DefaultHttpClient httpclient = new DefaultHttpClient();
		httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		//模拟浏览器，解决一些服务器程序只允许浏览器访问的问题
		httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
		httpclient.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
		httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset == null ? CHARSET_GBK : charset);
		httpclient.getParams().setParameter("http.connection.timeout",10000 );//设置链接超时 10s
		httpclient.getParams().setParameter("http.socket.timeout",20000 );//设置socket超时 20s  是指指定时间内服务器端没有反应，而如果两次反应之间的时间间隔小于设置的值是不算超时的
		httpclient.setHttpRequestRetryHandler(requestRetryHandler);
		return httpclient;
	}
	
	/**
	 * 
	 * 方法名：getDefaultHttpClient2 
	 * 功能：获取DefaultHttpClient实例，有设置代理，用于斗门测试环境
	 * 参数：
	 * @param charset
	 * @return
	 * 创建人：huanghsh  
	 * 创建时间：2012-2-24
	 */
	public static DefaultHttpClient getDefaultHttpClient2(final String charset){
		DefaultHttpClient httpclient = new DefaultHttpClient();
		httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		//模拟浏览器，解决一些服务器程序只允许浏览器访问的问题
		httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
		httpclient.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
		httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset == null ? CHARSET_GBK : charset);
		httpclient.getParams().setParameter("http.connection.timeout",5000 );//设置链接超时 5s
		httpclient.getParams().setParameter("http.socket.timeout",10000 );//设置socket超时 10s  是指指定时间内服务器端没有反应，而如果两次反应之间的时间间隔小于设置的值是不算超时的
		httpclient.setHttpRequestRetryHandler(requestRetryHandler);
		HttpHost httpHost = new HttpHost("192.168.13.19", 7777, null);
		httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,httpHost);
		return httpclient;
	}
	
	/**
	 * 释放HttpClient连接
	 *
	 * @param b
	 * 请求对象
	 * @param httpclient
	 * 			 client对象
	 */
	private static void abortConnection(final HttpRequestBase hrb, final HttpClient httpclient){
		if (hrb != null) {
			hrb.abort();
		}
		if (httpclient != null) {
			httpclient.getConnectionManager().shutdown();
		}
	}
	
	/**
	 * 从给定的路径中加载此 KeyStore
	 *
	 * @param url
	 * keystore URL路径
	 * @param password
	 * keystore访问密钥
	 * @return keystore 对象
	 */
	private static KeyStore createKeyStore(final URL url, final String password)
			throws KeyStoreException, NoSuchAlgorithmException,	CertificateException, IOException {
		if (url == null) {
			throw new IllegalArgumentException("Keystore url may not be null");
		}
		KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
		InputStream is = null;
		try {
			is = url.openStream();
			keystore.load(is, password != null ? password.toCharArray() : null);
		} finally {
			if (is != null){
				is.close();
				is = null;
			}
		}
		return keystore;
	}
	
	/**
	 * 将传入的键/值对参数转换为NameValuePair参数集
	 *
	 * @param paramsMap
	 * 参数集, 键/值对
	 * @return NameValuePair参数集
	 */
	private static List<NameValuePair> getParamsList(Map<String, String> paramsMap) {
		if (paramsMap == null || paramsMap.size() == 0) {
			return null;
		}
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		for (Map.Entry<String, String> map : paramsMap.entrySet()) {
			params.add(new BasicNameValuePair(map.getKey(), map.getValue()));
		}
		return params;
	}
	public static void main(String[] args) throws NetServiceException, InterruptedException {
//			Thread.sleep(100);
			Map<String, String> p=new HashMap<String, String>();
			p.put("encoding", "GBK");
			p.put("methodName", "real_charge");
//			String xmlbody=HttpclientUtil.postXML("http://127.0.0.1/msweb/servlet/T2", p,"fdafkjdafjdsafkj","GBK");
		int i=0;
		int total=0;
		while(i<2){
		String head="<bb><in0>123</in0> <in1>456</in1></bb>";
		Thread.sleep(1000);	
		long start = System.currentTimeMillis();
//			String xmlbody=HttpclientUtil.postXML("http://127.0.0.1/msweb/servlet/T2", p,"fdafkjdafjdsafkj","GBK");
			String xmlbody=HttpclientUtil.postSOAP("http://127.0.0.1/h/services/hzt", head, "GBK");
			long end = System.currentTimeMillis();
			int cost=(int) (end-start);
			System.out.println("i:"+i+" "+cost+"ms");
			i++;
			total=total+cost;
			System.out.println("--------"+total);
			System.out.println(xmlbody);
		}
		System.out.println("--------"+total);
	}
}